package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.controller.param.ShowWinningRecordsParam;
import com.example.lotterysystem.dao.dataobject.*;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.IDrawPrizeService;
import com.example.lotterysystem.service.dto.WinningRecordDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.example.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

@Slf4j
@Service
public class DrawPrizeServiceImpl implements IDrawPrizeService {

    private final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 2L;
    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private WinningRecordMapper winnerRecordMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void drawPrize(DrawPrizeParam param) {
        Map<String, String> map = new HashMap<>();
        map.put("messageId", String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(param));

        //发消息: 交换机, 绑定的key, 消息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, map);
        log.info("MQ消息发送成功: map={}", JacksonUtil.writeValueAsString(map));
    }

    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {
        //活动是否存在
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        //奖品是否存在
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getPrizeId(), param.getActivityId());
        if (null == activityDO || null == activityPrizeDO) {
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRICE_IS_EMPTY);
            log.error("校验参数失败:{}", ServiceErrorCodeConstants.ACTIVITY_OR_PRICE_IS_EMPTY.getMsg());
            return false;
        }
        //活动是否有效
        if (activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            log.error("校验参数失败:{}", ServiceErrorCodeConstants.ACTIVITY_COMPLETED.getMsg());
            return false;

        }
        //奖品是否有效
        if (activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())) {
//            throw new ServiceException(ServiceErrorCodeConstants.PRIZE_COMPLETED);
            log.error("校验参数失败:{}", ServiceErrorCodeConstants.PRIZE_COMPLETED.getMsg());
            return false;

        }
        //奖品数量和中奖者人数是否一致
        if (activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()) {
//            throw new ServiceException(ServiceErrorCodeConstants.WINNER_COUNT_ERROR);
            log.error("校验参数失败:{}", ServiceErrorCodeConstants.WINNER_COUNT_ERROR.getMsg());
            return false;

        }
        return true;
    }

    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        //查询相关信息:活动,人员,奖品,活动奖品
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        List<UserDO> userDOList = userMapper.batchSelectByIds(param.getWinnerList().stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList()));
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getPrizeId(), param.getActivityId());
        List<WinningRecordDO> winnerRecordDOList = userDOList.stream()
                .map(userDO -> {
                    WinningRecordDO winnerRecordDO = new WinningRecordDO();
                    winnerRecordDO.setActivityId(activityDO.getId());
                    winnerRecordDO.setActivityName(activityDO.getActivityName());
                    winnerRecordDO.setPrizeId(prizeDO.getId());
                    winnerRecordDO.setPrizeName(prizeDO.getName());
                    winnerRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winnerRecordDO.setWinnerEmail(userDO.getEmail());
                    winnerRecordDO.setWinnerId(userDO.getId());
                    winnerRecordDO.setWinnerName(userDO.getUserName());
                    winnerRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winnerRecordDO.setWinningTime(param.getWinningTime());
                    return winnerRecordDO;
                }).collect(Collectors.toList());
        //构造中奖者记录, 保存
        winnerRecordMapper.batchInsert(winnerRecordDOList);
        //缓存中奖者记录
        //1. 缓存奖品维度记录(WINNING_RECORDS_activityId_prizeId, winnerRecordDOList(奖品维度))
        cacheWinningRecords(param.getActivityId() + "_" + param.getPrizeId(), winnerRecordDOList, WINNING_RECORDS_TIMEOUT);
        //2. 缓存活动维度记录(WINNING_RECORDS_activityId, winnerRecordDOList(活动维度))
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            List<WinningRecordDO> winningRecordDOList = winnerRecordMapper.selectByActivityId(param.getActivityId());
            cacheWinningRecords(String.valueOf(param.getActivityId()), winningRecordDOList, WINNING_RECORDS_TIMEOUT);
        }
        return winnerRecordDOList;
    }

    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if(null == activityId){
            log.warn("要删除的中奖记录activityId为空");
        }
        //删除数据表
        winnerRecordMapper.deleteRecords(activityId, prizeId);

        //删除缓存
        if(null != prizeId){
            deleteWinningRecords(activityId + "_" + prizeId);
        }
        deleteWinningRecords(String.valueOf(activityId));
    }

    @Override
    public List<WinningRecordDTO> getRecords(ShowWinningRecordsParam param) {
        //查询redis
        String key = null == param.getPrizeId() ? String.valueOf(param.getActivityId())
                : param.getActivityId() + "_" + param.getPrizeId();
        List<WinningRecordDO> winningRecords = getWinningRecords(key);
        if(!CollectionUtils.isEmpty(winningRecords)){
            return convertToWinningRecordDTO(winningRecords);
        }
        //redis不存在查库, 并存在redis
        winningRecords = winnerRecordMapper.selectByActivityIdOrOrizeId(param.getActivityId(), param.getPrizeId());
        if(CollectionUtils.isEmpty(winningRecords)){
            log.info("查询的中奖记录为空:{}", JacksonUtil.writeValueAsString(param));
            return Arrays.asList();
        }
        cacheWinningRecords(key, winningRecords, WINNING_RECORDS_TIMEOUT);
        return convertToWinningRecordDTO(winningRecords);
    }

    private List<WinningRecordDTO> convertToWinningRecordDTO(List<WinningRecordDO> winningRecords) {

        if(CollectionUtils.isEmpty(winningRecords)){
            return Arrays.asList();
        }
        return winningRecords.stream().map(winningRecordDO -> {
            WinningRecordDTO winningRecordDTO = new WinningRecordDTO();
            winningRecordDTO.setWinnerId(winningRecordDO.getId());
            winningRecordDTO.setWinnerName(winningRecordDO.getWinnerName());
            winningRecordDTO.setPrizeName(winningRecordDO.getPrizeName());
            winningRecordDTO.setPrizeTier(ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()));
            winningRecordDTO.setWinningTime(winningRecordDO.getWinningTime());
            return winningRecordDTO;
        }).collect(Collectors.toList());
    }

    private void deleteWinningRecords(String key) {
        try{
            if(redisUtil.hasKey(WINNING_RECORDS_PREFIX + key)){
                redisUtil.del(WINNING_RECORDS_PREFIX + key);
            }
        }catch (Exception e){
            log.error("删除中奖记录缓存异常:key:{}", key, e);
        }
    }

    /**
     * 缓存中奖记录
     * @param key
     * @param winnerRecordDOList
     * @param time
     */
    private void cacheWinningRecords(String key,
                                     List<WinningRecordDO> winnerRecordDOList,
                                     Long time) {
        String value = JacksonUtil.writeValueAsString(winnerRecordDOList);
        try {
            if(!StringUtils.hasText(key) || CollectionUtils.isEmpty(winnerRecordDOList)){
                log.warn("要缓存的内容为空:key:{}, value:{}", key, value);
            }
            redisUtil.set(WINNING_RECORDS_PREFIX + key,
                    value, time);
        } catch (Exception e) {
            log.error("缓存中奖记录异常: key:{}, value:{}", WINNING_RECORDS_PREFIX + key, value, e);
        }
    }

    /**
     * 从缓存中获取中奖记录
     * @param key
     * @return
     */
    private List<WinningRecordDO> getWinningRecords(String key){
       try {
           if(StringUtils.hasText(key)){
               log.warn("要从缓存中查询的内容为空:key:{}", key);
               return Arrays.asList();
           }
           String value = redisUtil.get(WINNING_RECORDS_PREFIX + key);
           if(!StringUtils.hasText(value)) {
               return Arrays.asList();
           }
           return JacksonUtil.readListValue(value, WinningRecordDO.class);

       }catch (Exception e){
           log.error("从缓存中查询中奖记录异常: key:{}, value:{}", WINNING_RECORDS_PREFIX + key, e);
           return Arrays.asList();
       }
    }
}
